﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Pavilion.DesignerTool.ViewTools;
using System.ComponentModel;

namespace Pavilion.DesignerTool
{
    /// <summary>
    /// Interaction logic for LoadNodeEditorView.xaml
    /// </summary>
    public partial class LoadNodeEditorView : UserControl, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public double TreeViewItemWidth { get { return treeViewItemWidth; } set { treeViewItemWidth = value; NotifyPropertyChangedBase.OnPropertyChanged<double>(PropertyChanged, this, () => TreeViewItemWidth); } }

        private double treeViewItemWidth;
        private LoadNodeEditorViewModel viewModel;

        public LoadNodeEditorView()
        {
            InitializeComponent();

            base.DataContext = viewModel = new LoadNodeEditorViewModel();
        }

        public void SetLoadNode(LoadNodeWrapper loadNodeWrapper)
        {
            viewModel.SetLoadNode(loadNodeWrapper);
        }

        private void UpdateTreeViewItemWidth(double offset = 30)
        {
            TreeViewItemWidth = treeView.ActualWidth / 2 - offset;
        }

        private void AddGameItemMenuItem_Click(object sender, RoutedEventArgs e)
        {
            viewModel.AddGameItem();
        }

        private void AddPropertyMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (e.OriginalSource == sender)
                return;

            MenuItem menuItem = e.OriginalSource as MenuItem;
            PropertyCategory propertyCategory = (sender as FrameworkElement).DataContext as PropertyCategory;
            (propertyCategory.AssociatedItem as GameDataWrapper).AddProperty((string)menuItem.Header);
        }

        private void RemovePropertyMenuItem_Click(object sender, RoutedEventArgs e)
        {
            PropertyItemBase propertyItem = (sender as FrameworkElement).DataContext as PropertyItemBase;
            (propertyItem.AssociatedItem as GameDataWrapper).RemoveProperty(propertyItem.PropertyName);
        }

        private void RemoveGameDataMenuItem_Click(object sender, RoutedEventArgs e)
        {
            FrameworkElement framework = PavilionVisualTreeHelper.GetParentOfContextMenu<FrameworkElement>(sender as MenuItem);
            TreeViewItem item = PavilionVisualTreeHelper.GetAncestor<TreeViewItem>(framework);
            GameItemToolsInfo gameItemsToolsInfo = PavilionVisualTreeHelper.GetAncestor<TreeViewItem>(item).DataContext as GameItemToolsInfo;
            string gameToolName = (framework.DataContext as PropertyCategory).AssociatedItem.ToString();

            gameItemsToolsInfo.RemoveGameTool(gameToolName);
        }

        private void AddGameDataMenuItem_Click(object sender, RoutedEventArgs e)
        {
            FrameworkElement framework = PavilionVisualTreeHelper.GetParentOfContextMenu<FrameworkElement>(sender as MenuItem);
            GameItemToolsInfo gameItemsToolsInfo = framework.DataContext as GameItemToolsInfo;
            string gameToolName = ((e.OriginalSource as FrameworkElement).DataContext as Type).Name;

            gameItemsToolsInfo.AddGameTool(gameToolName);
        }

        private void TextBox_FocusableChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            TextBox textBox = (TextBox)sender;

            if (textBox.Focusable)
            {
                textBox.Focus();
                textBox.SelectAll();
            }
        }

        private void RemoveGameItemMenuItem_Click(object sender, RoutedEventArgs e)
        {
            viewModel.RemoveGameItem((sender as FrameworkElement).DataContext as GameItemToolsInfo);
        }

        private void TreeView_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateTreeViewItemWidth();
        }
    }

    public class LoadNodeEditorViewModel
    {
        public NotifiableCollection<GameItemToolsInfo> GameItemToolsInfoCollection { get; private set; }
        private LoadNodeWrapper loadNodeWrapper;

        public LoadNodeEditorViewModel()
        {
            this.GameItemToolsInfoCollection = new NotifiableCollection<GameItemToolsInfo>();
        }

        public void SetLoadNode(LoadNodeWrapper loadNodeWrapper)
        {
            this.loadNodeWrapper = loadNodeWrapper;

            GameItemToolsInfoCollection.Clear();

            if (loadNodeWrapper == null)
                return;

            foreach (GameItemWrapper gameItemWrapper in loadNodeWrapper.GameItemCollection)
                GameItemToolsInfoCollection.Add(new GameItemToolsInfo(gameItemWrapper, loadNodeWrapper));
        }

        public void AddGameItem()
        {
            string gameItemName = GameEngine.GetUniqueName(loadNodeWrapper.LoadNode.GameItemCollection, "GameItem");
            GameItemWrapper gameItemWrapper = loadNodeWrapper.AddGameItem(gameItemName);

            GameItemToolsInfoCollection.Add(new GameItemToolsInfo(gameItemWrapper, loadNodeWrapper));
        }

        public void RemoveGameItem(GameItemToolsInfo gameItemToolsInfo)
        {
            GameItemToolsInfoCollection.Remove(gameItemToolsInfo);
            loadNodeWrapper.RemoveGameItem(gameItemToolsInfo.GameItemName);
        }
    }

    public class GameItemToolsInfo
    {
        public string GameItemName { get { return gameItemWrapper.Name; } set { SetGameItemName(value); } }
        public NotifiableCollection<PropertyCategory> GameToolProperties { get; private set; }
        public IEnumerable<Type> GameDataTypeCollection { get { return GameEngine.ObtainAllTypes<GameToolBase>("Pavilion.GameTools.dll"); } }

        private LoadNodeWrapper loadNodeWrapper;
        private GameItemWrapper gameItemWrapper;

        public GameItemToolsInfo(GameItemWrapper gameItemWrapper, LoadNodeWrapper loadNodeWrapper)
        {
            this.gameItemWrapper = gameItemWrapper;
            this.loadNodeWrapper = loadNodeWrapper;

            GameToolProperties = new NotifiableCollection<PropertyCategory>();
            UpdateProperties();
        }

        private void UpdateProperties()
        {
            foreach (GameDataWrapper gameDataWrapper in loadNodeWrapper.ObtainGameItem(GameItemName).GameToolCollection)
                LoadGameDataWrapper(gameDataWrapper);
        }

        private void LoadGameDataWrapper(GameDataWrapper gameDataWrapper)
        {
            PropertyCategory propertyCategory = new PropertyCategory(gameDataWrapper);
            gameDataWrapper.PropertyAdded += new Action<GameDataWrapper, string>(GameDataWrapper_PropertyAdded);
            gameDataWrapper.PropertyRemoved += new Action<GameDataWrapper, string>(GameDataWrapper_PropertyRemoved);

            foreach (string propertyName in gameDataWrapper.PropertiesCollection)
                propertyCategory.AddProperty(new GameDataPropertyItem(gameDataWrapper, propertyName));

            GameToolProperties.Add(propertyCategory);
        }

        private void UnloadGameDataWrapper(GameDataWrapper gameDataWrapper)
        {
            gameDataWrapper.PropertyAdded -= new Action<GameDataWrapper, string>(GameDataWrapper_PropertyAdded);
            gameDataWrapper.PropertyRemoved -= new Action<GameDataWrapper, string>(GameDataWrapper_PropertyRemoved);

            PropertyCategory propertyCategory = GameToolProperties.First(x => x.AssociatedItem == gameDataWrapper);
            GameToolProperties.Remove(propertyCategory);
        }

        private PropertyCategory ObtainePropertyCategory(GameDataWrapper gameDataWrapper)
        {
            return GameToolProperties.First(x => x.AssociatedItem == gameDataWrapper);
        }

        private void GameDataWrapper_PropertyAdded(GameDataWrapper gameDataWrapper, string propertyName)
        {
            ObtainePropertyCategory(gameDataWrapper).AddProperty(new GameDataPropertyItem(gameDataWrapper, propertyName));
        }

        private void GameDataWrapper_PropertyRemoved(GameDataWrapper gameDataWrapper, string propertyName)
        {
            ObtainePropertyCategory(gameDataWrapper).RemoveProperty(propertyName);
        }

        public void AddGameTool(string gameToolName)
        {
            GameDataWrapper wrapper = loadNodeWrapper.ObtainGameItem(GameItemName).AddGameTool(gameToolName, loadNodeWrapper);
            LoadGameDataWrapper(wrapper);
        }

        public void RemoveGameTool(string gameToolName)
        {
            GameDataWrapper wrapper = loadNodeWrapper.ObtainGameItem(GameItemName).ObtainGameTool(gameToolName);
            loadNodeWrapper.ObtainGameItem(GameItemName).RemoveGameTool(gameToolName, loadNodeWrapper);
            UnloadGameDataWrapper(wrapper);
        }

        private void SetGameItemName(string value)
        {
            if (value == GameItemName)
                return;

            loadNodeWrapper.RenameGameItem(GameItemName, value);
        }
    }
}
